package com.ruoyi.basicData.ruleSetting.service.impl;

import com.ruoyi.basicData.ruleSetting.domain.DocumentRules;
import com.ruoyi.basicData.ruleSetting.domain.VehicleIdRules;
import com.ruoyi.basicData.ruleSetting.service.IAccessorySupplierRelevanceService;
import com.ruoyi.basicData.ruleSetting.service.IDocumentRulesService;
import com.ruoyi.basicData.ruleSetting.service.IVehicleIdRulesService;
import com.ruoyi.basicData.ruleSetting.service.RuleSettingService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SequenceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.UUID;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Yuan
 * @description
 * @date 2024/1/25
 */
@Service
@RequiredArgsConstructor
public class RuleSettingServiceImpl implements RuleSettingService {
    private final RedisCache redisCache;
    private final IDocumentRulesService documentRulesService;
    private final IVehicleIdRulesService vehicleIdRulesService;
    private final IAccessorySupplierRelevanceService accessorySupplierRelevanceService;
    Map<String,String> separators = new HashMap<>(3+1,1f);

    @PostConstruct
    void init() {
        separators.put("01", "-");
        separators.put("02", "_");
        separators.put("03", "");
    }

    /**
     *
     * @param clazz
     * @param docType 字典中的值，通过值就可定位到数据
     * @return
     */
    @Override
    public String genDocId(Class clazz, String docType) {
        //生成唯一ID,统一业务使用类锁
        synchronized (clazz) {
            //判断缓存中是否存在
            //缓存30分钟
            String cacheKey = CacheConstants.RULE_DOC_ID_KEY + docType;
            DocumentRules documentRules = redisCache.getCacheObject(cacheKey);
            if(documentRules == null) {
                documentRules = documentRulesService.selectDocumentRulesByDocType(docType);
                //考虑搜索不到数据情况
                if(Objects.isNull(documentRules)) {
                    //缓存，避免穿透
                    redisCache.setCacheObject(cacheKey, documentRules, 30 * 60 * 1000 + RandomUtils.nextInt(0,5000), TimeUnit.MILLISECONDS);
                    return IdUtils.fastUUID();
                }
                //添加随机数，使缓存时间过期不一致，避免缓存击穿
                redisCache.setCacheObject(cacheKey, documentRules, 30 * 60 * 1000 + RandomUtils.nextInt(0,5000), TimeUnit.MILLISECONDS);
            }
            int codeSegment = Integer.parseInt(documentRules.getCodeSegment());
            String separator = documentRules.getSeparator();
            String constRecord = documentRules.getConstRecord();
            String dateFormat = documentRules.getDateFormat();
            int serialNumLength = Integer.parseInt(documentRules.getSerialNumLength()+"");
            String encodeJoinSequence = documentRules.getEncodeJoinSequence();
            return uniqueId(docType, separator, codeSegment, constRecord, dateFormat, serialNumLength, encodeJoinSequence);
        }
    }

    @Override
    public String genBikeId(Class clazz) {
        synchronized (clazz) {
            String cacheKey = CacheConstants.RULE_BIKE_ID_KEY;
            VehicleIdRules vehicleIdRule = redisCache.getCacheObject(cacheKey);
            if(vehicleIdRule == null) {
                List<VehicleIdRules> vehicleIdRules = vehicleIdRulesService.selectVehicleIdRulesList(null);
                if(Objects.isNull(vehicleIdRules) || vehicleIdRules.size() == 0) {
                    //避免缓存穿透
                    redisCache.setCacheObject(cacheKey, vehicleIdRule);
                    return IdUtils.fastUUID();
                }
                vehicleIdRule = vehicleIdRules.get(0);
                redisCache.setCacheObject(cacheKey, vehicleIdRule);
            }
            int codeSegment = Integer.parseInt(vehicleIdRule.getCodeSegment());
            String separator = vehicleIdRule.getSeparator();
            String constRecord = vehicleIdRule.getConstRecord();
            String dateFormat = vehicleIdRule.getDateFormat();
            int serialNumLength = Integer.parseInt(vehicleIdRule.getSerialNumLength()+"");
            String encodeJoinSequence = vehicleIdRule.getEncodeJoinSequence();
            return uniqueId("vehicle",separator, codeSegment, constRecord, dateFormat, serialNumLength, encodeJoinSequence);
        }
    }

    //返回值为String，而非StringBuilder，不会出现逃逸
    private String uniqueId(String type, String separator, int codeSegment, String constRecord,
                            String dateFormat, int serialNumLength, String encodeJoinSequence) {
        String[] joinSeq = encodeJoinSequence.split(",");
        Date date = new Date();
        //分隔符
        separator = separators.get(separator);
        Map<String, String> data = new HashMap<>(codeSegment+1, 1f);
        if(StringUtils.isNotEmpty(constRecord)) data.put("01", constRecord);
        if(StringUtils.isNotEmpty(dateFormat)) data.put("02", DateUtils.parseDateToStr(dateFormat, date));

        StringBuilder prefix = new StringBuilder();
        for (String dataKey : joinSeq) {
            if(!"03".equals(dataKey)) {
                prefix.append(data.get(dataKey)).append(separator);
            }
        }
        RedisTemplate redisTemplate = redisCache.redisTemplate;
        if(StringUtils.isNotEmpty(dateFormat)){
            //否则，将日期作为Key，1作为Value重置，并设置第二天0点过期
            if (redisCache.getCacheObject(prefix.toString()) != null) {
                redisTemplate.opsForValue().increment(prefix.toString(), 1);
            } else {
                redisTemplate.opsForValue().set(prefix.toString(), 1);
                redisTemplate.expireAt(prefix.toString(), SequenceUtil.getMidnightDate());
            }
            //移位运算符<<,创造yyyyMMdd0001格式
            return (prefix + SequenceUtil.getSequence(String.valueOf(redisTemplate.opsForValue().get(prefix.toString())) , serialNumLength));
        }else{
            //否则，将日期作为Key，1作为Value重置，并设置第二天0点过期
            String key = type + "_id";
            if (redisCache.getCacheObject(key) != null) {
                redisTemplate.opsForValue().increment(key, 1);
            } else {
                redisTemplate.opsForValue().set(key, 1);
            }
            //移位运算符<<,创造yyyyMMdd0001格式
            return (prefix + SequenceUtil.getSequence(String.valueOf(redisTemplate.opsForValue().get(key)) , serialNumLength));
        }
    }
}
